######################################## Qt 5.12 Widget桌面开发 ######################################## Qt基础 ====== Qt编译安装 ---------- 本次实验环境为VS2019+QT5.12.4 1. | 首先打开PowerShell,执行下面命令以创建编译环境: | C:\Qt\Qt5.12.4\5.12.4\msvc2017_64\bin\qtenv2.bat | C:\"Program Files (x86)"\"Microsoft Visual Studio"\2019\Community\VC\Auxiliary\Build\vcvars64.bat。 2. 进入QT源代码文件夹:C:\Qt\Qt5.12.4\5.12.4\Src。 3. | 进行配置: | C:\Qt\Qt5.12.4\5.12.4\Src\qtbase\configure.bat -prefix E:\QT -hostprefix E:\QT\5.12.4\\ -opensource -confirm-license -debug-and-release -developer-build -plugin-manifests -pch -silent -dbus-linked -dbus-runtime -accessibility -system-proxies -direct2d -gif -ico -sql-odbc -sql-sqlite 4. 进行编译:C:\Qt\Qt5.12.4\Tools\QtCreator\bin\jom.exe 5. 安装:C:\Qt\Qt5.12.4\Tools\QtCreator\bin\jom.exe install 6. 清除构建文件:C:\Qt\Qt5.12.4\Tools\QtCreator\bin\jom.exe clean Qt附加程序 ========== 程序发布程序:windeployqt ------------------------- 要完成应用程序的发布,请遵循以下步骤: 1. 以Release模式编译项目。 |image0| 2. 前往编译目录下获取编译文件,该命令通常与项目目录位于同一层次: |image1| +----------------------------+ | │ .qmake.stash | | | | │ Makefile | | | | │ Makefile.Debug | | | | │ Makefile.Release | | | | │ | | | | ├─debug | | | | └─release | | | | │ main.o | | | | │ moc_predefs.h | | | | │ moc_widget.cpp | | | | │ moc_widget.o | | | | │ widget.o | | | | │ **Widget_NoUI_Test.exe** | | | | │ | | | | └─stable.h.gch | +----------------------------+ 3. 将编译后的exe提取到一个空目录下。 4. 从开始菜单中选择对应版本的环境工具: |image2| 5. 运行windeployqt命令: |image3| 6. 此时的程序运行库全部在该目录下,将目录打包发布即可。 Qml预览工具:qmlscene --------------------- qmlscene用于预览qml脚本文件: 1. 打开Qt的环境工具: |image4| 2. 切换到qml文件目录下运行qmlscene命令: .. |image5| - 你也可以直接打开qmlscene.exe,打开后按要求选择qml文件。 元对象系统 ========== 信号和槽 -------- 属性系统 -------- 事件系统 ======== 事件驱动模型 ------------ 事件(Event)是由操作系统或程序自身发出的\ **任务请求**\ 。当\ **键盘点击**\ 、\ **窗口重绘**\ 、\ **鼠标移动**\ 、\ **计时结束……**\ 时都会产生对应事件。 事件驱动模型基本思路如下\ :sup:`[1.]`\ : 1. 由程序维护一个\ **事件(消息)队列。** 2. 当事件产生时(如鼠标按下)向事件队列中添加事件。 3. 由\ **事件循环**\ 不断从事件队列中取出事件。 4. 根据取出事件的类别,调用对应的\ **事件处理函数**\ 。 **事件驱动模型**\ 是\ **I/O编程范式(事件驱动模型**\ 、\ **单线程模型**\ 、\ **多线程模型)**\ 的一种,其目的是为了解决I/O交互问题。大多数GUI都应用了事件驱动模型。因此,可以说: “GUI是由事件驱动的” Qt中的事件 ---------- 事件对象 ~~~~~~~~ **Qt中的事件均继承自QEvent**\ 。常用事件对象的继承结构如下: |image6| 当事件发生时,Qt将创建一个事件对象,并将其添加到事件循环中。 使用事件循环 ~~~~~~~~~~~~ 要使用Qt应用程序中,你必须创建一个QApplication对象并调用其exec()函数: +-----------------------------------+ | int main(int argc, char \*argv[]) | | | | { | | | | QApplication a(*argc*, argv); | | | | return a.exec(); | | | | } | +-----------------------------------+ 在调用QApplication的exec函数后,程序将进入事件循环并监听程序的事件。 回调函数 ~~~~~~~~ 回调(CallBack)函数,用于处理特定事件。 消息循环不断从消息队列中取出事件,然后根据事件类型调用对应的事件处理函数。 要自定义事件处理,必须重写类的事件处理函数。例如QWidget的鼠标事件: ============ ============================================== virtual void mouseDoubleClickEvent(QMouseEvent \*\ *event*) virtual void mouseMoveEvent(QMouseEvent \*\ *event*) virtual void mousePressEvent(QMouseEvent \*\ *event*) virtual void mouseReleaseEvent(QMouseEvent \*\ *event*) ============ ============================================== 事件传递 -------- 添加事件 ~~~~~~~~ 事件的添加可以调用以下函数: +----------------------------------------------------------------------+ | QCoreApplication::**postEvent**\ (**QObject** \*\ *receiver*, | | **QEvent** \*\ *event*) | +======================================================================+ | QCoreApplication::**sendEvent**\ (**QObject** \*\ *receiver*, | | **QEvent** \*\ *event*) | +----------------------------------------------------------------------+ 两个函数的不同在于: postEvent将事件添加到事件循环末尾,而sendEvent则通过notify()将事件直接发送给receiver。 事件的传递 ~~~~~~~~~~ Qt中事件产生后首先加入事件循环,然后 1. 进入QApplication::notify()进行第一次事件分发。 2. 被分发的事件首先进入QObject对象的事件过滤器(EventFilter)中进行事件过滤。 3. 被过滤的事件进入QObject::event(),并根据事件类型调用事件处理函数(CallBack)。 4. 若事件被接受(通过调用Accpet()),则事件传递结束;若事件被忽略(ignore())则将事件传递给父控件处理。 .. 传递过程如下: |这里写图片描述| 处理事件的方法 -------------- **Qt 提供了5个级别的事件处理和事件过滤方法。\ [5.]** 1. **重新实现特殊的事件处理器** 重新实现像 mousePressEvent()、keyPressEvent()和paintEvent()这样的事件处理器是到现在为止最常用的事件处理方式。我们已经看到很多有关这种处理方式的例子了。 2. **重新实现QObject::event** 通过event()函数的、重新实现,可以在这些事件到达特定的事件处理器之前处理它们。这种方式常用于覆盖 Tab键的默认意义。 **那些没有特定事件处理器的不常见类型的事件中(例如,Qevent::HoverEnter()。当重新实现event()时,必须对那些没有明确处理的情况调用其基类的event()函数。**\ [1]_ 3. **在QObect中安装事件过滤器** 对象一旦使用installEventFilter()注册过,用于目标对象的所有事件都会首先发送给这个监视对象的eventFilter()函数。如果在同一个对象上安装了多个事件处理器,那么就会按照安装顺序逆序,从最近安装的到最先安装的,依次激活这些事件处理器。 4. **在QApplication 对象中安装事件过滤器** 一且在qApp(唯一的那个QApplication对象)中注册了事件过滤器,那么应用程序中每个对象的每个事件都会在发送到其他事件过滤器之前,先发送给这个eventFilter()函数。这种处理方式对于调试是非常有用的。它也可以用来处理那些发送给失效窗口部件的鼠标事件,因为QApplication常都会忽略这些事件。 5. **子类化QApplication 并且重新实现 notify()** Qt调用 Qapplication::notify()来发送一个事件。重新实现这个函数是在事件过滤器得到所有事件之前获得它们的唯一方式。但事件过滤器通常更有用,因为可以同时有多个事件过滤器,而notify()函数却只能有一个。 安装事件过滤器 -------------- 每个Qobject都有一个\ **eventFilter**\ 函数,重写该函数并为控件安装事件过滤器。 [2]_ 该函数的完整定义如下: ==== ==================================================== bool **eventFilter**\ (QObject \*watched, QEvent \*event) ==== ==================================================== 当watched的事件event被处理后,返回true,否则返回false。 +---------------------------------------------------------------------+ | // widget.h | | | | class **Widget** : public QWidget | | | | { | | | | Q_OBJECT | | | | public: | | | | **Widget**\ (QWidget \*parent = nullptr); | | | | ~\ **Widget**\ ()override; | | | | // QObject interface | | | | bool **eventFilter**\ (QObject \*watched, QEvent \*event) override; | | | | private: | | | | QPushButton*pushButton=new QPushButton("QPushButton",this); | | | | }; | +=====================================================================+ | // widget.cpp | | | | Widget::**Widget**\ (QWidget \*parent) | | | | : QWidget(parent) | | | | { | | | | resize(500,500); | | | | pushButton->installEventFilter(this); | | | | this->installEventFilter(this); | | | | } | | | | Widget::~\ **Widget**\ () | | | | { | | | | } | | | | bool Widget::**eventFilter**\ (QObject \*watched, QEvent \*event) | | | | { | | | | if(watched==pushButton){ | | | | if(event->type()==QEvent::MouseButtonPress){ | | | | QMouseEvent*e=static_cast(event); | | | | qDebug()<type(); | | | | } | | | | } | | | | return false; | | | | } | +---------------------------------------------------------------------+ |image8| 保证密集事件时主线程的相应 -------------------------- 当处理事件时,可能会由于某个特定的事件造成阻塞,导致程序无反应,解决这个问题的办法为:\ :sup:`[5.]` 1. 多线程 使用多线程将耗时的事件放入子线程处理,从而防止了主线程阻塞。 2. 调用processEvents() 在耗时代码中频繁调用 Qapplication::processEvents()。这个函数告诉Qt。处理所有那些还没有被处理的各类事件,然后再将控制权返还给调用者。实际上,Qapplication::èxec() 就是一个不停调用processEvents()函数的while循环。 [3]_ 事件与信号和槽机制的区别 ------------------------ 1. 信号发射后,立即调用槽函数,没有消息队列。而事件发生后,要经过消息队列排序和消息传递过程。 2. 自定义事件处理必须重写对应回调函数(CallBack),而信号和槽无需重写类。 其他组件 ======== 添加应用图标 ------------ |image9| 调试和日志系统 ============== 调试函数 -------- qDebug() ~~~~~~~~ 要使用qDebug() [4]_函数,你需要包含QDebug头文件。 [5]_ qDebug有两种使用方式: 1. qDebug(const char \*message, ...) 和普通的printf()类似,只是输出数字、文本到调试器。 2. qDebug()<< 后面可以接Qobject对象用于打印对象信息,也可以接字符串和数字。 +-------------------------------------+ | QWidget widget; | | | | qDebug()<<"&widget信息:"<<&widget; | +-------------------------------------+ 效果如下: |image10| qWarning ~~~~~~~~ 使用方法与qDebug()\ :sup:`6.1.1` 相同。具体行为与宏QT_FATAL_WARNINGS的值有关。 qCritical() ~~~~~~~~~~~ 使用方法与qDebug()\ :sup:`6.1.1` 相同 [6]_。具体行为与宏QT_FATAL_CRITICALS的值有关。 qFatal()函数 ~~~~~~~~~~~~ qFatal()函数的使用方式与printf()相同,但是qFatal()函数在调用后终止程序运行。 [7]_ 只有当程序发生严重错误、不得不退出时才建议使用qFatal()。 qInfo ~~~~~ 使用方法与qDebug()\ :sup:`6.1.1` 相同 [8]_。具体行为与宏QT_NO_INFO_OUTPUT的值有关。 调试宏 ------ Q_ASSERT(bool test)宏 ~~~~~~~~~~~~~~~~~~~~~ 若test为false,将输出断言警告,并终止程序: +-----------------+ | int a=1; | | | | Q_ASSERT(a==0); | +-----------------+ 效果如下: |image11| Q_ASSERT_X() ~~~~~~~~~~~~ 该宏的完整定义如下: **Q_ASSERT_X**\ (**bool** *test*, const **char** \*\ *where*, const **char** \*\ *what*)。 当test为false是发出断言警告并终止程序。 +----------------------------------------+ | int a=1; | | | | Q_ASSERT_X(a==0,"a的值","a的值不为0"); | +----------------------------------------+ 效果如下: |image12| CHECK_PTR() ~~~~~~~~~~~ 检查指针的值。 |image13| 消息句柄 -------- QtMessageHandler 文件读取 ======== QFileInfo --------- +----------------------------------------------------------------+ | #include | | | | #include | | | | #include | | | | int **main**\ (int argc, char \*argv[]) | | | | { | | | | QCoreApplication a(*argc*, argv); | | | | QString filename=R"(C:/Users/31951/Desktop/2.txt)"; | | | | QFileInfo fileinfo(filename); | | | | qDebug()<<"fileName: "<setLayout(HLlayout); | | | | this->setWindowTitle("ListWidget"); | | | | HLlayout->addWidget(new QLabel("浏览器",this)); | | | | HLlayout->addWidget(LWwidget); | | | | // 添加元素 | | | | LWwidget->addItem("Chrome"); // 第一种方法 | | | | SLlist<<"Chrome"<<"Firefox"<<"IE"<<"Maxthon"; // 第二种方法 | | | | LWwidget->addItems(SLlist); | | | | // 第三种方法 | | | | QListWidgetItem \*LWIitem=new | | QListWidgetItem(QIcon("C:\\FireFox.ico"),"FireFox"); | | | | LWwidget->addItem(LWIitem); | | | | // LWwidget->setViewMode(QListView::IconMode); // | | 以图标形式显示 [10]_ | | | | } | +----------------------------------------------------------------------+ 显示效果如下: |ListWidget| QtreeWidget ~~~~~~~~~~~ +-----------------------------------------------------+ | Widget::**Widget**\ (QWidget \*parent) | | | | : QWidget(parent) | | | | { | | | | QHBoxLayout*HLlayout=new QHBoxLayout(this); | | | | QTreeWidget*TWwidget=new QTreeWidget(this); | | | | // 设置布局 | | | | this->setLayout(HLlayout); | | | | this->setWindowTitle("TreeWidget"); | | | | HLlayout->addWidget(new QLabel("TreeWidget",this)); | | | | HLlayout->addWidget(TWwidget); | | | | // 添加元素 | | | | QTreeWidgetItem*root=new QTreeWidgetItem( | | | | TWwidget, QStringList("Node1")); | | | | QTreeWidgetItem*Node1=new QTreeWidgetItem( | | | | root,QStringList("Node1.1")); | | | | new QTreeWidgetItem( | | | | Node1,QStringList("Node1.1.1")); | | | | // 设置选择框 | | | | Node1->setCheckState(0,Qt::Checked); | | | | // 设置表头 | | | | TWwidget->setHeaderLabel("章节"); | | | | //TWwidget->setHeaderHidden(true); // 隐藏表头 | | | | } | +-----------------------------------------------------+ 显示效果如下: |ListWidget2| QtableWidget ~~~~~~~~~~~~ +-----------------------------------------------------------------+ | Widget::**Widget**\ (QWidget \*parent) | | | | : QWidget(parent) | | | | { | | | | QHBoxLayout*HLlayout=new QHBoxLayout(this); | | | | QTableWidget*TabWwidget=new QTableWidget(this); | | | | QStringList headers; | | | | // 设置布局 | | | | this->setLayout(HLlayout); | | | | this->setWindowTitle("TableWidget"); | | | | HLlayout->addWidget(new QLabel("TableWidget",this)); | | | | HLlayout->addWidget(TabWwidget); | | | | // 设置表格大小 | | | | TabWwidget->setColumnCount(3); | | | | TabWwidget->setRowCount(5); | | | | // 添加元素 | | | | TabWwidget->setItem(0,0,new QTableWidgetItem(QString("001"))); | | | | TabWwidget->setItem(0,1,new QTableWidgetItem(QString("002"))); | | | | TabWwidget->setItem(1,0,new QTableWidgetItem(QString("小明"))); | | | | // 设置表头 | | | | headers<<"ID"<<"Name"<<"Age"; | | | | TabWwidget->setHorizontalHeaderLabels(headers); | | | | } | +-----------------------------------------------------------------+ 显示效果如下: |TableWidget| View ~~~~ View和ViewWidget的继承关系如下: |image18| View的使用需要结合模型(Model)使用,而ViewWidget不需要使用模型,适用于少量数据的显示。 模型和模型索引 -------------- QstringListModel ~~~~~~~~~~~~~~~~ +--------------------------------------------------------------------+ | Widget::**Widget**\ (QWidget \*parent) | | | | : QWidget(parent) | | | | { | | | | QHBoxLayout*HLlayout=new QHBoxLayout(this); | | | | QListView*LVview=new QListView(this); | | | | QStringListModel \*model=new QStringListModel; | | | | // 设置布局 | | | | this->setLayout(HLlayout); | | | | this->setWindowTitle("TableWidget"); | | | | HLlayout->addWidget(new QLabel("TableWidget",this)); | | | | HLlayout->addWidget(LVview); | | | | // 添加模型数据 | | | | QStringList data{ | | | | "Appel", | | | | "HuaWei", | | | | "XiaoMi", | | | | "Vivo", | | | | "OPPO" | | | | }; | | | | model->setStringList(data); | | | | LVview->\ *setModel*\ (model); // 设置View的模型 | | | | // 在尾部插入数据 | | | | model->insertRow(model->\ *rowCount*\ ()); | | | | QModelIndex index=model->\ *index*\ (model->\ *rowCount*\ ()-1,0); | | | | model->\ *setData*\ (index,"Google"); | | | | // 排序 | | | | model->\ *sort*\ (5,Qt::AscendingOrder); | | | | } | +--------------------------------------------------------------------+ 效果如下: |TableWidget2| 模型索引和角色 ~~~~~~~~~~~~~~ 自定义委托 ---------- +--------------------------------------------------------+ | // spinboxdelegate.h文件 | | | | class **SpinBoxDelegate** : public QStyledItemDelegate | | | | { | | | | Q_OBJECT | | | | public: | | | | **SpinBoxDelegate**\ (QObject*parent=nullptr); | | | | ~\ **SpinBoxDelegate**\ ()override=default; | | | | // QAbstractItemDelegate interface | | | | QWidget \*\ **createEditor**\ (QWidget \*parent, | | | | const QStyleOptionViewItem &option, | | | | const QModelIndex &index) const override; | | | | // QAbstractItemDelegate interface | | | | void **setEditorData**\ (QWidget \*editor, | | | | const QModelIndex &index) const override; | | | | void **setModelData**\ (QWidget \*editor, | | | | QAbstractItemModel \*model, | | | | const QModelIndex &index) const override; | | | | void **updateEditorGeometry**\ (QWidget \*editor, | | | | const QStyleOptionViewItem &option, | | | | const QModelIndex &index) const override; | | | | }; | +--------------------------------------------------------+ +--------------------------------------------------------------------+ | // spinboxdelegate.cpp文件 | | | | SpinBoxDelegate::**SpinBoxDelegate**\ (QObject*parent): | | | | QStyledItemDelegate (parent) | | | | { | | | | } | | | | QWidget \*SpinBoxDelegate::**createEditor**\ (QWidget \*parent, | | | | const QStyleOptionViewItem &option, | | | | const QModelIndex &index) const | | | | { | | | | Q_UNUSED(option) | | | | Q_UNUSED(index) | | | | QSpinBox*editor=new QSpinBox(parent); | | | | editor->setMinimum(0); | | | | editor->setMaximum(100); | | | | return editor; | | | | } | | | | void SpinBoxDelegate::**setEditorData**\ (QWidget \*editor, | | | | const QModelIndex &index) const | | | | { | | | | QVariant value=index.model()->\ *data*\ (index,Qt::EditRole); | | | | QSpinBox*spinbox=static_cast(editor); | | | | spinbox->setValue(value.toInt()); | | | | } | | | | void SpinBoxDelegate::**setModelData**\ (QWidget \*editor, | | | | QAbstractItemModel \*model, | | | | const QModelIndex &index) const | | | | { | | | | QSpinBox*spinbox=static_cast(editor); | | | | spinbox->interpretText(); | | | | model->\ *setData*\ (index,spinbox->value(),Qt::EditRole); | | | | } | | | | void SpinBoxDelegate::**updateEditorGeometry**\ (QWidget \*editor, | | | | const QStyleOptionViewItem &option, | | | | const QModelIndex &index) const | | | | { | | | | Q_UNUSED(index); | | | | editor->setGeometry(option.rect); | | | | } | +--------------------------------------------------------------------+ 设置委托: +--------------------------------------------------------+ | // 设置委托 | | | | SpinBoxDelegate*SBDdelegate=new SpinBoxDelegate(this); | | | | LVview->setItemDelegate(SBDdelegate); | +--------------------------------------------------------+ 效果如下: |image20| 视图选择 -------- 视图的选择使用了QItemSelectionModel类,View和ViewWidget已经内置了选择模型,可以使用selectionModel()来获取选择模型。 选择模式 ~~~~~~~~ 内置的选择模式有以下几种: **枚举:SelectionMode** =================== == =============================================== 常数 值 描述 =================== == =============================================== QAbstractItemView:: 1 能且仅能选择一项,可以不选 SingleSelection QAbstractItemView:: 4 只能连续选取。可以不选 ContiguousSelection QAbstractItemView:: 3 按中Shift键时为连续选择,按中Ctrl键时为间隔选择 ExtendedSelection QAbstractItemView:: 2 一次选择一项,可选择多项,再次点击取消选择 MultiSelection QAbstractItemView:: 0 不可选择 NoSelection =================== == =============================================== 要设置选择模式,请使用setSelectionMode()函数。 排序过滤 -------- 模型内置了两种排序方式: **枚举:Qt::SortOrder** =================== == ============== 常数 值 描述 =================== == ============== Qt::AscendingOrder 0 按字母升序排列 Qt::DescendingOrder 1 按字母降序排列 =================== == ============== 要进行排序,请使用QstringListModel:: *sort()*\ 函数。 代理 ~~~~ 更高级的排序过滤功能由代理类:QSortFilterProxyModel提供。 QSortFilterProxyModel提供了基于正则表达式、Unix通配符、普通文本的过滤功能和基于函数的排序功能。 在使用代理后,MVC的显示机理变为: +--------------------------------------------------------------+ | QListView*LVview=new QListView(this); | | | | QStringListModel \*model=new QStringListModel; | | | | QSortFilterProxyModel*filtermodel=new QSortFilterProxyModel; | | | | LVview->\ *setModel*\ (filtermodel); | | | | // 添加模型数据 | | | | QStringList data{ | | | | "Appel", | | | | "HuaWei", | | | | "XiaoMi", | | | | "Vivo", | | | | "OPPO" | | | | }; | | | | model->setStringList(data); | | | | // 过滤数据 | | | | filtermodel->\ *setSourceModel*\ (model); | | | | filtermodel->setFilterRegExp(R"(^A)"); | +--------------------------------------------------------------+ 显示效果如下: |TableWidget3| 自定义模型 ---------- 自定义只读模型 ~~~~~~~~~~~~~~ SQL === 进程通信 ======== 剪切板 ------ MIME数据类型 ~~~~~~~~~~~~ MIME是一种类型标准,MIME定义的标准可以获取数据的形式,进而进行解析,常用的MIME类型如下 [11]_: =================== =========== MIME类型 数据类型 =================== =========== text/html Html text/plain 纯文本 text/uri-list Url application/x-color 颜色 video/x-msvideo Avi视频 image/jpg Jpg格式图片 =================== =========== Qt通过QMimeData类实现对MIME数据的储存与获取。事实上,只要保证数据可以解析,可以人为规定一个MIME数据类型,即使没有标准库会的承认。 .. _剪切板-1: 剪切板 ~~~~~~ QClipboard类通过储存MIME类型数据储存数据,实现了剪切板的操作。通过QApplication::clipboard()可以获取应用程序的剪切板指针,从而对剪切板进行操作。 不同的系统对剪切板概念的支持不尽相同,在Windows和macOS中,剪切板属于全局资源,任何对剪切板的操作都是对全局剪切板的操作。 [12]_ +---------------------------------------------------+ | Widget::**Widget**\ (QWidget \*parent) | | | | : QWidget(parent) | | | | { | | | | QHBoxLayout*HLlayout=new QHBoxLayout(this); | | | | this->setLayout(HLlayout); | | | | QLabel*lbl=new QLabel; | | | | HLlayout->addWidget(lbl); | | | | QMimeData*mimedata=new QMimeData; | | | | mimedata->setData("text/plain","

Hello

"); | | | | QClipboard*clipboard=QApplication::clipboard(); | | | | clipboard->setMimeData(mimedata); | | | | lbl->setText(clipboard->text()); | | | | } | +---------------------------------------------------+ 效果如下: |Widget_NoUI_Test|\ |image23| 拖放 ---- 从拖放中读取数据 ~~~~~~~~~~~~~~~~ +-------------------------------------------------------------+ | // widget.h | | | | class **Widget** : public QWidget | | | | { | | | | Q_OBJECT | | | | public: | | | | **Widget**\ (QWidget \*parent = nullptr); | | | | ~\ **Widget**\ ()override; | | | | // QWidget interface | | | | protected: | | | | void **dragEnterEvent**\ (QDragEnterEvent \*event)override; | | | | void **dropEvent**\ (QDropEvent \*event)override; | | | | private: | | | | QTextBrowser*textbrown=new QTextBrowser(this); | | | | QHBoxLayout*layout=new QHBoxLayout(this); | | | | }; | +-------------------------------------------------------------+ +------------------------------------------------------------+ | // widget.cpp | | | | Widget::**Widget**\ (QWidget \*parent) | | | | : QWidget(parent) | | | | { | | | | layout->addWidget(textbrown); | | | | setLayout(layout); | | | | setAcceptDrops(true); | | | | textbrown->setAcceptDrops(false); | | | | } | | | | Widget::~\ **Widget**\ () | | | | { | | | | } | | | | void Widget::**dragEnterEvent**\ (QDragEnterEvent \*event) | | | | { | | | | if(event->mimeData()->hasText()){ | | | | event->acceptProposedAction(); | | | | } | | | | } | | | | void Widget::**dropEvent**\ (QDropEvent \*event) | | | | { | | | | QListurls=event->mimeData()->urls(); | | | | if(urls.isEmpty())return; | | | | QString filename=urls.first().toLocalFile(); | | | | if(filename.isEmpty()) return; | | | | QFile file(filename); | | | | file.\ *open*\ (QFile::ReadOnly); | | | | if(!file.isOpen()){ | | | | qDebug()<<"文件打开失败"; | | | | return; | | | | } | | | | textbrown->setText(file.readAll()); | | | | file.\ *close*\ (); | | | | } | +------------------------------------------------------------+ 效果如下: |image24| 该软件在加载时没有读取文件,又没有定义打开文件的接口,且无法编辑。只能通过拖放来更改窗口内容。 自定义拖放数据 ~~~~~~~~~~~~~~ 要自定义拖放数据,有以下几种方法:\ :sup:`[4.]` 1. 将自定义数据作为 QByteArray 对象,使用 QMimeData::setData()函数作为二进制 2. 数据存储到 QMimeData 中,然后使用 QMimeData::data()读取 3. 继承 QMimeData,重写其中的 formats()和 retrieveData()函数操作自定义数据 4. 如果拖放操作仅仅发生在同一个应用程序,可以直接继承 QMimeData,然后使用任 意合适的数据结构进行存储 数据库 ====== 数据解析 ======== 解析XML ------- 使用DOM解析XML ~~~~~~~~~~~~~~ 使用SAM解析XML ~~~~~~~~~~~~~~ 解析JSON -------- 使用Qjson解析JSON ~~~~~~~~~~~~~~~~~ 使用QjsonDocument解析JSON ~~~~~~~~~~~~~~~~~~~~~~~~~ 网络 ==== 线程 ==== 绘图 ==== 双缓冲 ------ 请查看C++ GUI Qt4编程(第二版)第五章 Qt基本组件 ========== 菜单 ---- QMenu用于提供主界面菜单、菜单栏菜单、弹出菜单。 QMenu的非继承属性共有五个: ===================== ======================================== 属性名 作用 ===================== ======================================== icon 菜单图标 title 菜单标题 separatorsCollapsible 连续的分割线是否折叠(默认为true) [13]_ toolTipsVisible 是否显示toolTips tearOffEnabled 菜单是否可分离出一个副本(默认为false) ===================== ======================================== QMenu常用的函数有以下几种: ========= ================= ======================== 返回值 函数 作用 ========= ================= ======================== QAction\* addAction() 重载函数,用于添加菜单项 QMeun\* addMenu() 重载函数,用于添加子菜单 QAction\* addSeparator() 添加一个分割线 QAction\* insertMenu() 插入一个菜单 QAction\* insertSeparator() 插入一个分割线 void popup() 在指定位置弹出菜单 ========= ================= ======================== 常用信号: ============= ================== 信号名 何时发射 ============= ================== aboutToHide() 用户尝试隐藏菜单前 aboutToShow() 用户尝试显示菜单前 hovered() 有菜单项处于活动前 triggered() 有菜单项被激活时 \ ============= ================== 按钮 ---- QPushButton ~~~~~~~~~~~ QPushButton常用的属性如下: +---------------+-----------------------------------------------------+ | 属性名 | 作用 | +===============+=====================================================+ | autoDefault | 当为true时 | | | ,将会有更大的“推荐尺寸”,Dialog下的按钮默认为true | +---------------+-----------------------------------------------------+ | default | 当为ture时,按钮获 | | | 取焦点后将按Enter键视为鼠左键单击。Dialog默认为true | +---------------+-----------------------------------------------------+ | checkable | 按钮是否是可勾选的 | +---------------+-----------------------------------------------------+ | checked | 按钮是否是勾选过的 | +---------------+-----------------------------------------------------+ | shortcut | 设置按钮快捷键 | +---------------+-----------------------------------------------------+ | text | 按钮上的文字 | +---------------+-----------------------------------------------------+ | autoExclusive | 处于同一个parent的按钮是否是互斥的,默认为false | +---------------+-----------------------------------------------------+ | down | 按钮是否可以被按下 | +---------------+-----------------------------------------------------+ | flat | 按钮是否为扁平的 | +---------------+-----------------------------------------------------+ 常用函数 ============== ========= ==================== 返回值 函数 作用 ============== ========= ==================== void setMenu() 设置按钮菜单 QButtonGroup\* group() 返回按钮所在的按钮组 ============== ========= ==================== 常用信号: ========== ============== 信号名 何时发射 ========== ============== clicked() 按钮点击后 pressed() 按钮按下时 released() 按钮释放时 toggled() 勾选状态改变时 ========== ============== 公有槽: ========== ======== 槽名 作用 ========== ======== showMenu() 显示菜单 ========== ======== QCheckBox ~~~~~~~~~ QCheckBox定义了一个带有QLabel的复选框。 主要属性如下: ======== ============================================================ 属性名 作用 ======== ============================================================ tristate tri-state,复选框是否有三个状态,默认为false,仅有两个状态。 ======== ============================================================ 枚举类型: **Qt::CheckState** ==================== == ========== 描述 值 解释 ==================== == ========== Qt::Unchecked 0 未选中状态 Qt::PartiallyChecked 1 半选中状态 Qt::Checked 2 选中状态 ==================== == ========== 常用函数 +--------+-----------------+-----------------------------------------+ | 返回值 | 函数 | 作用 | +========+=================+=========================================+ | void | setCheckState() | 设置checkbox的复选状 | | | | 态,该函数适用于tristate为true时使用。 | +--------+-----------------+-----------------------------------------+ | void | setChecked() | 设置checkbox | | | | 是否已选择,适用于tristate为false时使用 | +--------+-----------------+-----------------------------------------+ 常用信号: ============== ============== 信号名 何时发射 ============== ============== clicked() 按钮点击后 pressed() 按钮按下时 released() 按钮释放时 toggled() 勾选状态改变时 stateChanged() 状态改变时 ============== ============== QRadioButton ~~~~~~~~~~~~ QRadioButton提供了带有QLabel的单选框,其除了继承QAbstractButton的属性和函数外,没有额外的属性与函数。 QToolButton ~~~~~~~~~~~ QToolbutton提供了命令或选项的快捷方式,通常用于工具栏。 枚举: **QToolButton::ToolButtonPopupMode** +------------------------------+----+-------------------------------+ | 描述 | 值 | 解释 | +==============================+====+===============================+ | QToolButton::DelayedPopup | 0 | 点击 | | | | 时触发默认动作,长按显示菜单 | +------------------------------+----+-------------------------------+ | QToolButton::MenuButtonPopup | 1 | 显示一 | | | | 个箭头代表该按钮提供一个菜单 | +------------------------------+----+-------------------------------+ | QToolButton::InstantPopup | 2 | 当按下工具按钮时, | | | | 菜单会立即显示出来。在此模式 | | | | 下,不会触发按钮本身的操作。 | +------------------------------+----+-------------------------------+ **Qt::ArrowType** ============== == ======== 描述 值 描述 ============== == ======== Qt::NoArrow 0 无箭头 Qt::UpArrow 1 向上箭头 Qt::DownArrow 2 向下箭头 Qt::LeftArrow 3 向左箭头 Qt::RightArrow 4 向右箭头 ============== == ======== **Qt::ToolButtonStyle** ============================ == ========================= 描述 值 解释 ============================ == ========================= Qt::ToolButtonIconOnly 0 仅显示图标 Qt::ToolButtonTextOnly 1 仅显示文字 Qt::ToolButtonTextBesideIcon 2 文字显示在图标旁边 Qt::ToolButtonTextUnderIcon 3 文字显示在图标下面 Qt::ToolButtonFollowStyle 4 样式跟随QStyle::StyleHint ============================ == ========================= 主要属性如下: =============== ========================================= 属性名 作用 =============== ========================================= arrowType 设置箭头属性 autoRaise 定义了auto-raising是否可用,默认未false。 popupMode 定义了菜单的弹出方式 toolButtonStyle 定义了按钮风格 =============== ========================================= 常用函数 =================== ================= ============ 返回值 函数 作用 =================== ================= ============ void setMenu() 设置按钮菜单 QAction\* defaultAction() 返回默认动作 Qt::ToolButtonStyle toolButtonStyle() 返回按钮风格 =================== ================= ============ 常用信号: ========================= ================== 信号名 何时发射 ========================= ================== triggered(QAction*action) 指定action被触发时 toolButtonStyle 定义了按钮风格 ========================= ================== 常用槽 +--------+-----------------------------+-----------------------------+ | 返回值 | 函数 | 作用 | +========+=============================+=============================+ | void | QToo | 设置按钮的默认动作,若已 | | | lButton::setDefaultAction() | 有动作,则设置以下属性的一 | | | | 种:checkable、checked、ena | | | | bled、font、icon、popupMode | | | | (assuming the action has a | | | | menu)、statusTi | | | | p、text、toolTip、whatsThis | +--------+-----------------------------+-----------------------------+ | void | showMenu() | 显示菜单 | +--------+-----------------------------+-----------------------------+ QCommandLinkButton ~~~~~~~~~~~~~~~~~~ QCommandLinkButton继承自QPushButton,用于提供一个链接按钮,用于指示打开新的页面。常用于一组互斥选项,其功能与“单选+下一步”按钮组合的作用相同。 |image25| 主要属性如下: =========== ============================================== 属性名 作用 =========== ============================================== description 为按钮选项提供描述,其文本通常比按钮的Text小。 flat 按钮是否扁平化,默认为false [14]_ =========== ============================================== 按钮组合 -------- 按钮组合实现了对一组按钮的控制,主要用于按钮组件的互斥和关联。按钮组合由\ **QButtonGroup**\ 类实现,其继承自QObject,本质为按钮容器。 [15]_ 主要属性如下: ========= ====================================================== 属性名 作用 ========= ====================================================== exclusive 容器内按钮是否互斥,默认为true,仅有一个按钮可以被选中 ========= ====================================================== 常用函数 +----------------------+----------------------+----------------------+ | 返回值 | 函数 | 作用 | +======================+======================+======================+ | QAbstractButton \* | QB | 返回指定id的按钮,如 | | | uttonGroup::button() | 按钮不存在,则返回0 | +----------------------+----------------------+----------------------+ | Q | QBu | 返回容器内的所有按钮 | | List | | | +----------------------+----------------------+----------------------+ | QAbstractButton \* | QButtonGr | 返回被点击过的按钮, | | | oup::checkedButton() | 若无,则返回0 [18]_ | +----------------------+----------------------+----------------------+ | int | QButt | 返回被点击的按钮 | | | onGroup::checkedId() | 的id,若无,则返回-1 | +----------------------+----------------------+----------------------+ | int | QButtonGroup::id() | 返回指定按钮的id | +----------------------+----------------------+----------------------+ | void | Q | 设置指定 | | | ButtonGroup::setId() | 按钮的id,id不能为-1 | +----------------------+----------------------+----------------------+ | void | QButtonG | 从按 | | | roup::removeButton() | 钮组合中移除指定按钮 | +----------------------+----------------------+----------------------+ 常用信号: +----------------------------------------------+----------------------+ | 信号名 | 何时发射 | +==============================================+======================+ | buttonClicked(QAbstractButton \*button) | button被点击时 | +----------------------------------------------+----------------------+ | buttonClicked(int id) | 任何按钮被点击时 | +----------------------------------------------+----------------------+ | buttonPressed(QAbstractButton \*button) | button被按下时 | +----------------------------------------------+----------------------+ | buttonPressed(int id) | 任何按钮被按下时 | +----------------------------------------------+----------------------+ | buttonReleased(QAbstractButton \*button) | 任何按钮被释放时 | +----------------------------------------------+----------------------+ | buttonReleased(int id) | 任何按钮被释放时 | +----------------------------------------------+----------------------+ | buttonToggled(QAbstractButton \*button, bool | button状态切换时 | | checked) | | +----------------------------------------------+----------------------+ | buttonToggled(int id, bool checked) | 任何按钮状态被切换时 | +----------------------------------------------+----------------------+ 滚动条 ------ 使用QScrollArea创建滚动 ~~~~~~~~~~~~~~~~~~~~~~~ 使用QScrollArea作为Widget容器可以对Widget附加水平和垂直滚动条。 QScrollArea的继承关系如下: +-----------------------------------------------+ | Widget::**Widget**\ (QWidget \*parent) | | | | : QWidget(parent) | | | | { | | | | QHBoxLayout*layout=new QHBoxLayout; | | | | setLayout(layout); | | | | QScrollArea*scrollarea=new QScrollArea(this); | | | | layout->addWidget(scrollarea); | | | | QLabel*lbl=new QLabel(this); | | | | lbl->resize(600,600); | | | | scrollarea->setWidget(lbl); | | | | } | +-----------------------------------------------+ 效果如下: |Widget_NoUI_Test2| 在无需滚动条时,QScrollArea的滚动条会自动隐藏。QScrollArea的滚动条仅在需要的方向上出现(水平或垂直方向)。这种方式又被称为“\ **按需滚动**\ ”。 使用QScrollBar创建滚动条 ~~~~~~~~~~~~~~~~~~~~~~~~ 容器 ---- QToolBox ~~~~~~~~ QToolBox对QWidget提供了列状组织,每次只显示QWidget列表中的一个: |image27| QToolBox以item组织列表,每个item代表了一个QWidget。其继承关系如下: 主要属性如下: ============ ============================ 属性名 作用 ============ ============================ count QToolBox item的总数 currentIndex QToolBox当前显示的item的索引 ============ ============================ 常用函数 +-----------+-----------------+--------------------------------------+ | 返回值 | 函数 | 作用 | +===========+=================+======================================+ | int | addItem() | 添加item | +-----------+-----------------+--------------------------------------+ | QWidget\* | currentWidget() | 返回当前显示的item对应的QWidget。 | +-----------+-----------------+--------------------------------------+ | int | indexof() | 返回特定QWidget对应的索引 | +-----------+-----------------+--------------------------------------+ | int | insertItem() | 将item插入带特定位置,若越 | | | | 界则插入到底部。返回插入的item的索引 | +-----------+-----------------+--------------------------------------+ | bool | isItemEnabled() | 如果启用了位 | | | | 置索引项,则返回true;否则返回false。 | +-----------+-----------------+--------------------------------------+ 常用信号: ============================================= ====================== 信号名 何时发射 ============================================= ====================== currentChanged(int index) 显示的item发生改变时 customContextMenuRequested(const QPoint &pos) 请求上下文菜单时 [19]_ ============================================= ====================== QTabBar ~~~~~~~ QTabBar提供了一个标签栏(tab bar)。Qt提供了一个更简便的的类:QTabWidget。两者均继承自QWidget。 枚举: 1. **QTabBar::ButtonPosition** ================== == ==================== 描述 值 解释 ================== == ==================== QTabBar::LeftSide 0 widget位于标签栏左侧 QTabBar::RightSide 1 widget位于标签栏右侧 ================== == ==================== 2. **QTabBar::SelectionBehavior** ======================= == ============================ 描述 值 解释 ======================= == ============================ QTabBar::SelectLeftTab 0 当标签被移除时切换到左侧标签 QTabBar::SelectRightTab 1 当标签被移除时切换到右侧标签 ======================= == ============================ 3. **QTabBar::Shape** ======================== == ============================================ 描述 值 解释 ======================== == ============================================ QTabBar::RoundedNorth 0 阴影边界。expanding为false时横线位于下方 QTabBar::RoundedSouth 1 阴影边界。expanding为false时横线位于下方 QTabBar::RoundedWest 2 阴影边界。标签栏左侧竖排显示,文字从右向左看 QTabBar::RoundedEast 3 阴影边界。标签栏左侧竖排显示,文字从左向右看 QTabBar::TriangularNorth 4 线条边界。expanding为false时横线位于下方 QTabBar::TriangularSouth 5 线条边界expanding为false时横线位于下方 QTabBar::TriangularWest 6 线条边界标签栏左侧竖排显示,文字从右向左看 QTabBar::TriangularEast 7 线条边界标签栏左侧竖排显示,文字从左向右看 ======================== == ============================================ 主要属性如下: +---------------------------+-----------------------------------------+ | 属性名 | 作用 | +===========================+=========================================+ | autoHide | 标签栏少于两个时自动隐藏,默认为false | +---------------------------+-----------------------------------------+ | expanding | 自动 | | | 调正大小以占满所有可用空间,默认为true | +---------------------------+-----------------------------------------+ | changeCurrentOnDrag | 当接受拖放事 | | | 件时自动切换到被drop的标签。默认为false | +---------------------------+-----------------------------------------+ | count | 标签栏的数量 | +---------------------------+-----------------------------------------+ | movable | 标签栏是 | | | 否可以通过拖动调整显示顺序。默认为false | +---------------------------+-----------------------------------------+ | currentIndex | 返回当前 | | | 显示的标签页,若没有显示的标签页返回-1 | +---------------------------+-----------------------------------------+ | selectionBehaviorOnRemove | 设定移除后显示的标签页,与 | | | \ **QTabBar::SelectionBehavior**\ 有关 | +---------------------------+-----------------------------------------+ | documentMode | 该属性用于提示标签栏的绘制 | +---------------------------+-----------------------------------------+ | shape | 根据\ | | | **QTabBar::Shape**\ 设定标签栏显示位置 | +---------------------------+-----------------------------------------+ | drawBase | 标签栏是否绘制底线,默认为true | +---------------------------+-----------------------------------------+ | tabsClosable | 是否在标签栏显示关闭按钮,默认为false | +---------------------------+-----------------------------------------+ | elideMode | 越界时如何省略文本,与\ **Qt::T | | | extElideMode**\ 有关,默认值依赖于style | +---------------------------+-----------------------------------------+ | usesScrollButtons | 标签栏过多时是否显示按钮 | | | 以滚动显示标签栏。默认值依赖于style设置 | +---------------------------+-----------------------------------------+ 常用函数 +------------+----------------+--------------------------------------+ | 返回值 | 函数 | 作用 | +============+================+======================================+ | int | addTab() | 添加标签页 | +------------+----------------+--------------------------------------+ | int | insertTab() | 插入标签页 | +------------+----------------+--------------------------------------+ | void | setTabButton() | 在指定索引的\ **QTabBar::But | | | | tonPosition**\ 位置上添加一个QWidget | +------------+----------------+--------------------------------------+ | void | setTabData() | 为指定位置设置数据 | +------------+----------------+--------------------------------------+ | int | tabAt() | 返回 | | | | 指定坐标处的标签页索引,若无则返回-1 | +------------+----------------+--------------------------------------+ | QWidget \* | tabButton() | | +------------+----------------+--------------------------------------+ 常用槽 ====== ========================== ============ 返回值 函数 作用 ====== ========================== ============ void setCurrentIndex(int index) 切换当前标签 ====== ========================== ============ 常用信号: ============================== ================ 信号名 何时发射 ============================== ================ currentChanged(int index) 当前标签页改变时 tabBarClicked(int index) 标签页被点击时 tabBarDoubleClicked(int index) 标签页被双击时 tabCloseRequested(int index) 标签页被关闭前 tabMoved(int from, int to) 标签页被移动时 ============================== ================ QTabWidget ~~~~~~~~~~ QTabWidget是Qt根据QTabBar为用户提供的便利类。 枚举: 1. **QTabWidget::TabPosition** ================= == ==== 描述 值 解释 ================= == ==== QTabWidget::North 0 QTabWidget::South 1 QTabWidget::West 2 QTabWidget::East 3 ================= == ==== 2. **QTabWidget::TabShape** ====================== == ================== 描述 值 解释 ====================== == ================== QTabWidget::Rounded 0 圆形标签,默认选项 QTabWidget::Triangular 1 三角形标签 ====================== == ================== 主要属性如下: +-------------------+-------------------------------------------------+ | 属性名 | 作用 | +===================+=================================================+ | count | 标签栏的数量 | +-------------------+-------------------------------------------------+ | currentIndex | 当前标签的索引 | +-------------------+-------------------------------------------------+ | documentMode | 该属性用于提示标签栏的绘制 | +-------------------+-------------------------------------------------+ | elideMode | 越界时如何省略文本,与\ | | | **Qt::TextElideMode**\ 有关,默认值依赖于style | +-------------------+-------------------------------------------------+ | movable | 标签栏是否可以通过拖动调整显示顺序。默认为false | +-------------------+-------------------------------------------------+ | tabBarAutoHide | 标签栏少于两个时自动隐藏,默认为false | +-------------------+-------------------------------------------------+ | tabPosition | 描述了标签栏的位置,与\ | | | **QTabWidget::TabPosition**\ 有关,默认为North | +-------------------+-------------------------------------------------+ | tabShape | 根据 | | | \ **QTabWidget::TabShape**\ 设定标签栏显示位置 | +-------------------+-------------------------------------------------+ | tabsClosable | 是否在标签栏显示关闭按钮,默认为false | +-------------------+-------------------------------------------------+ | usesScrollButtons | 标签栏过多时是否 | | | 显示按钮以滚动显示标签栏。默认值依赖于style设置 | +-------------------+-------------------------------------------------+ 常用函数 ========== ==================== ======================================== 返回值 函数 作用 ========== ==================== ======================================== int addTab() 添加标签页 int insertTab() 插入标签页 QWidget \* cornerWidget() 返回位于\ **Qt::Corner**\ 处的标签widget int currentIndex() 返回当前标签页索引 QWidget \* currentWidget() 返回当前标签的widget void removeTab(int index) 移除指定标签 ========== ==================== ======================================== 常用槽 ====== ================================== ============ 返回值 函数 作用 ====== ================================== ============ void setCurrentIndex(int index) 切换当前标签 void setCurrentWidget(QWidget \*widget) 切换当前标签 ====== ================================== ============ 常用信号: ============================== ================ 信号名 何时发射 ============================== ================ currentChanged(int index) 当前标签页改变时 tabBarClicked(int index) 标签页被点击时 tabBarDoubleClicked(int index) 标签页被双击时 tabCloseRequested(int index) 标签页被关闭前 ============================== ================ 示例: 添加QTabWidget的关闭标签功能: +-------------------------------------------------+ | QTabWidget*tw=new QTabWidget(this); | | | | tw->addTab(new QPushButton(this),"btn1"); | | | | tw->addTab(new QPushButton(this),"btn2"); | | | | tw->setTabsClosable(true); | | | | connect(tw,&QTabWidget::tabCloseRequested, | | | | tw,&QTabWidget::removeTab); // 添加关闭标签功能 | +-------------------------------------------------+ |image28| QStackedWidget ~~~~~~~~~~~~~~ QStackedWidget继承自QFrame。为widget提供了栈式显示,同一时刻仅显示一个widget。 主要属性如下: ============ ====================== 属性名 作用 ============ ====================== count 储存了栈内widget的数量 currentIndex 当前显示的widget的索引 ============ ====================== 常用函数 ========== =============== ================ 返回值 函数 作用 ========== =============== ================ int addWidget() 添加widget int indexOf() 插入widget void removeWidget() 删除widget QWidget \* currentWidget() 当前显示的widget int indexOf() 指定widget的索引 QWidget \* widget 指定索引的widget ========== =============== ================ 常用槽 ====== ================================== ================ 返回值 函数 作用 ====== ================================== ================ void setCurrentIndex(int index) 设定显示的index void setCurrentWidget(QWidget \*widget) 设定显示的widget ====== ================================== ================ 常用信号: ========================= ====================== 信号名 何时发射 ========================= ====================== currentChanged(int index) 当前显示的widget改变时 widgetRemoved(int index) widget被移除时 ========================= ====================== 演示: +--------------------------------------------------+ | QHBoxLayout*HL_layout=new QHBoxLayout(this); | | | | QStackedWidget*sw=new QStackedWidget(this); | | | | HL_layout->addWidget(sw); | | | | sw->addWidget(new QPushButton("btn1",this)); | | | | sw->addWidget(new QPushButton("btn2",this)); | | | | sw->addWidget(new QPushButton("btn3",this)); | | | | sw->addWidget(new QPushButton("btn4",this)); | | | | auto btn_remove=new QPushButton("remove",this); | | | | auto btn_pre=new QPushButton("pre",this); | | | | auto btn_next=new QPushButton("next",this); | | | | HL_layout->addWidget(btn_pre); | | | | HL_layout->addWidget(btn_next); | | | | HL_layout->addWidget(btn_remove); | | | | connect(btn_remove,&QPushButton::clicked,[sw](){ | | | | sw->removeWidget(sw->currentWidget()); | | | | }); | | | | connect(btn_pre,&QPushButton::clicked,[sw](){ | | | | int index=sw->currentIndex()-1; | | | | if(index<0) index=sw->count()-1; | | | | qDebug()<<"pre:"<setCurrentIndex(index); | | | | }); | | | | connect(btn_next,&QPushButton::clicked,[sw](){ | | | | int index=sw->currentIndex()+1; | | | | if(index>sw->count()-1) index=0; | | | | qDebug()<<"next:"<setCurrentIndex(index); | | | | }); | +--------------------------------------------------+ |image29| QDockWidget ~~~~~~~~~~~ QDockWidget继承自QWidget,提供了一个dock widget,其既可以作为程序的一部分,又可单独分离成为一个桌面顶级窗口。 常用枚举 1. **QDockWidget::DockWidgetFeature** +----------------------+----------------------+----------------------+ | 描述 | 值 | 解释 | +======================+======================+======================+ | QDockWidget | 0x01 | dock | | ::DockWidgetClosable | | widget可以被关闭 | +----------------------+----------------------+----------------------+ | QDockWidge | 0x02 | dock | | t::DockWidgetMovable | | wid | | | | get可以在docks中移动 | +----------------------+----------------------+----------------------+ | QDockWidget: | 0x04 | dock | | :DockWidgetFloatable | | widget可 | | | | 以分离成一个独立窗口 | +----------------------+----------------------+----------------------+ | QDockWidget::DockWi | 0x08 | dock | | dgetVerticalTitleBar | | widget将 | | | | 标题栏垂直显示在左侧 | +----------------------+----------------------+----------------------+ | QDockWidget::A | DockWidgetClosab | (过时)该窗口可 | | llDockWidgetFeatures | le|DockWidgetMovable | 以被移动、附加、分离 | | | |DockWidgetFloatable | | +----------------------+----------------------+----------------------+ | QDockWidget:: | 0x00 | 该dock | | NoDockWidgetFeatures | | widget无 | | | | 法被移动、附加、分离 | +----------------------+----------------------+----------------------+ 主要属性如下: +--------------+------------------------------------------------------+ | 属性名 | 作用 | +==============+======================================================+ | allowedAreas | dock | | | widget可以被停靠的区域,其值与\ **Qt::DockWi | | | dgetArea**\ 有关,默认为\ **Qt::AllDockWidgetAreas** | +--------------+------------------------------------------------------+ | features | dock | | | wi | | | dget的特征,与\ **QDockWidget::DockWidgetFeature**\ | | | 有关,默认为\ **QDockWidget::AllDockWidgetFeatures** | +--------------+------------------------------------------------------+ | floating | dock widget是否可分离为一个独立的窗口 | +--------------+------------------------------------------------------+ | windowTitle | 窗口标题 | +--------------+------------------------------------------------------+ 常用函数 +------------+---------------------+---------------------------------+ | 返回值 | 函数 | 作用 | +============+=====================+=================================+ | void | setTitleBarWidget() | 使用一个自定义的widget作为dock | | | | widget的标题栏 | +------------+---------------------+---------------------------------+ | void | setWidget() | 设置dock widget的默认组件 | +------------+---------------------+---------------------------------+ | QAction \* | toggleViewAction() | 该QAction可用于控制该dock | | | | widget | +------------+---------------------+---------------------------------+ 常用信号: ===================== ========================= 信号名 何时发射 ===================== ========================= allowedAreasChanged() 允许停靠区域发生改变时 dockLocationChanged() 停靠区域发生改变时 featuresChanged() dock widget特征发生改变时 topLevelChanged() floating属性改变时 visibilityChanged() dock widget被隐藏/显示时 ===================== ========================= 例: +---------------------------------------------------------+ | QHBoxLayout*HL_layout=new QHBoxLayout(this); | | | | QDockWidget*dw=new QDockWidget(this); | | | | HL_layout->addWidget(dw); | | | | dw->setWidget(new QPushButton("btn1",this)); | | | | dw->setTitleBarWidget(new QPushButton("btn_bar",this)); | +---------------------------------------------------------+ 效果如下: |image30| QMdiArea ~~~~~~~~ QMdiArea中文译为“多文档界面”,其提供了一个类似桌面的区域,其区域内可显示多个子窗口。并且所有子窗口的最大化、最小化等效果以QMdiArea为基准。其继承关系如下: 1. **QMdiArea::AreaOption** +------------------------------+-----+------------------------------+ | 描述 | 值 | 解释 | +==============================+=====+==============================+ | QMdiArea::DontM | 0x1 | 当活动 | | aximizeSubWindowOnActivation | | 子窗口最大化时,最大化下一个 | | | | 被激活的子窗口。默认为true。 | +------------------------------+-----+------------------------------+ 2. **QMdiArea::ViewMode** ======================= == ========================== 描述 值 解释 ======================= == ========================== QMdiArea::SubWindowView 0 带有frames的子窗口(默认) QMdiArea::TabbedView 1 带有标签栏的子窗口 ======================= == ========================== 3. **QMdiArea::WindowOrder** +-------------------------------+----+-------------------------------+ | 描述 | 值 | 解释 | +===============================+====+===============================+ | QMdiArea::CreationOrder | 0 | 按子窗口的创造顺序返回 | +-------------------------------+----+-------------------------------+ | QMdiArea::StackingOrder | 1 | 按子窗口的前 | | | | 后顺序返回,最靠前的排在最后 | +-------------------------------+----+-------------------------------+ | QMd | 2 | 按子窗口的活动顺序返回 | | iArea::ActivationHistoryOrder | | | +-------------------------------+----+-------------------------------+ 主要属性如下: +-----------------+---------------------------------------------------+ | 属性名 | 作用 | +=================+===================================================+ | activationOrder | 该属性储存了子窗 | | | 口列表的属性,与\ **QMdiArea::WindowOrder**\ 有关 | +-----------------+---------------------------------------------------+ | background | 该属性定义了子窗口的背景画刷。默认为灰色 | +-----------------+---------------------------------------------------+ | documentMode | 此属性定义选项 | | | 栏是否在选项视图模式中设置为文档模式。默认为true | +-----------------+---------------------------------------------------+ | tabPosition | 定义了标签模式下标签 | | | 栏的位置,与\ **QTabWidget::TabPosition**\ 有关。 | +-----------------+---------------------------------------------------+ | tabShape | 定义了标签形状,与\ **QTabWi | | | dget::TabShape**\ 有关。默认为QTabWidget::Rounded | +-----------------+---------------------------------------------------+ | tabsClosable | 标签是否可关闭,默认为false | +-----------------+---------------------------------------------------+ | tabsMovable | 标签是否可移动,默认为false | +-----------------+---------------------------------------------------+ | viewMode | 该属性定义了子 | | | 窗口的显示方式,与\ **QMdiArea::ViewMode**\ 有关 | +-----------------+---------------------------------------------------+ 常用函数 +-----------------------+--------------------+-----------------------+ | 返回值 | 函数 | 作用 | +=======================+====================+=======================+ | QMdiSubWindow \* | addSubWindow() | 添加子窗口 | +-----------------------+--------------------+-----------------------+ | void | removeSubWindow() | 移除子窗口 | +-----------------------+--------------------+-----------------------+ | QMdiSubWindow \* | activeSubWindow() | 返回当前被激 | | | | 活的子窗口.若无返回0 | +-----------------------+--------------------+-----------------------+ | QMdiSubWindow \* | currentSubWindow() | 返回当 | | | | 前子窗口,若无则返回0 | +-----------------------+--------------------+-----------------------+ | QList | | **QMdiArea::WindowOr | | | | der**\ 的顺序返回列表 | +-----------------------+--------------------+-----------------------+ | | | | +-----------------------+--------------------+-----------------------+ 常用槽 ====== =========================== ======================== 返回值 函数 作用 ====== =========================== ======================== void activateNextSubWindow() 激活下一个子窗口 void activatePreviousSubWindow() 激活上一个子窗口 void cascadeSubWindows() 层叠子窗口 void closeActiveSubWindow() 关闭活动的子窗口 void closeAllSubWindows() 关闭所有子窗口 void setActiveSubWindow() 设置活动子窗口 void tileSubWindows() 以平铺模式排列所有子窗口 ====== =========================== ======================== 常用信号: +----------------------------------+----------------------------------+ | 信号名 | 何时发射 | +==================================+==================================+ | subWindowActivated(QMdiSubWindow | window被激活后,, | | \*window) | 若windows=0,则QMdiArea反激活最 | | | 后一个活动窗口,此时没有激活窗口 | +----------------------------------+----------------------------------+ 例: +-------------------------------------------------+ | QHBoxLayout*HL_layout=new QHBoxLayout(this); | | | | QMdiArea*ma=new QMdiArea(this); | | | | HL_layout->addWidget(ma); | | | | ma->addSubWindow(new QPushButton("btn1",this)); | | | | ma->addSubWindow(new QPushButton("btn2",this)); | | | | ma->addSubWindow(new QPushButton("btn3",this)); | | | | ma->addSubWindow(new QPushButton("btn4",this)); | | | | ma->tileSubWindows(); | +-------------------------------------------------+ |image31| QMdiSubWindow ~~~~~~~~~~~~~ QMdiSubWindow继承自QWidget,代表了QMdiArea中的一个子窗口。 1. **QMdiSubWindow::SubWindowOption** +------------------------------+-----+------------------------------+ | 描述 | 值 | 解释 | +==============================+=====+==============================+ | QMd | 0x4 | 如果启用此选项 | | iSubWindow::RubberBandResize | | ,则使用rubber来表示子窗口的 | | | | 大纲,用户将调整此选项的大小 | | | | ,而不是子窗口本身的大小。因 | | | | 此,子窗口将保持其原始位置和 | | | | 大小,直到完成调整大小操作, | | | | 此时它将接收一个QResizeEvent | | | | 。默认情况下,此选项是禁用的 | +------------------------------+-----+------------------------------+ | Q | 0x8 | 如果启用此选项,则使 | | MdiSubWindow::RubberBandMove | | 用rubber来表示子窗口的大纲, | | | | 用户将移动此控件而不是子窗口 | | | | 本身。因此,子窗口将保持在原 | | | | 来的位置,直到移动操作完成, | | | | 此时将向窗口发送QMoveEvent。 | | | | 默认情况下,此选项是禁用的。 | +------------------------------+-----+------------------------------+ 主要属性如下: ================== ======================================= 属性名 作用 ================== ======================================= keyboardPageStep 使用page键移动窗口时的步长,默认为20px keyboardSingleStep 使用键盘箭头移动窗口时的步长,默认为5px ================== ======================================= 常用函数 +-------------+-----------------+------------------------------------+ | 返回值 | 函数 | 作用 | +=============+=================+====================================+ | QMdiArea \* | mdiArea() | 返回控件所处的MdieArea | +-------------+-----------------+------------------------------------+ | void | setSystemMenu() | 设置子窗口的默认系统菜单 | +-------------+-----------------+------------------------------------+ | void | setOption() | 设置子窗口的选项,与\ **QMdi | | | | SubWindow::SubWindowOption**\ 相关 | +-------------+-----------------+------------------------------------+ 常用槽 +--------+------------------+----------------------------------------+ | 返回值 | 函数 | 作用 | +========+==================+========================================+ | void | showShaded() | 调用此函数使子窗口进入阴影模式。 | | | | 当子窗口为阴影时,只有标题栏是可见的。 | +--------+------------------+----------------------------------------+ | void | showSystemMenu() | 显示系统菜单 | +--------+------------------+----------------------------------------+ 常用信号: ==================== =================== 信号名 何时发射 ==================== =================== aboutToActivate() 子窗口被激活前 windowStateChanged() windows state改变后 ==================== =================== 例: +--------------------------------------------------+ | QHBoxLayout*HL_layout=new QHBoxLayout(this); | | | | QMdiArea*ma=new QMdiArea(this); | | | | HL_layout->addWidget(ma); | | | | ma->addSubWindow(new QPushButton("btn1",this)); | | | | ma->addSubWindow(new QPushButton("btn2",this)); | | | | ma->addSubWindow(new QPushButton("btn3",this)); | | | | ma->addSubWindow(new QPushButton("btn4",this)); | | | | ma->tileSubWindows(); | | | | foreach(QMdiSubWindow\* x,ma->subWindowList()){ | | | | connect(x,&QMdiSubWindow::aboutToActivate,[x](){ | | | | qDebug()<<"子窗口"<addWidget(username); | | | | layout->addWidget(passwd); | | | | layout->addWidget(itOk); | | | | // 设置QLineEdit的属性 | | | | username->setClearButtonEnabled(true); | | | | username->setAcceptDrops(true); | | | | passwd->setEchoMode(QLineEdit::Password); | | | | passwd->setClearButtonEnabled(true); | | | | connect(username,&QLineEdit::editingFinished,[this](){ | | | | passwd->setFocus(); | | | | }); | | | | connect(passwd,&QLineEdit::editingFinished,[this](){ | | | | emit itOk->clicked(); | | | | }); | | | | connect(itOk,&QPushButton::clicked,[](){ | | | | qDebug("clicked"); | | | | }); | +--------------------------------------------------------+ 定时器 ------ 布局管理 ======== QBoxLayout ---------- QFormLayout ----------- QGridLayout ----------- QStackedLayout -------------- 数据 ==== QCache ------ QDataWidgetMapper ----------------- Qt美化 ====== QSS --- QStyleOption ------------ QStyleOptionButton ~~~~~~~~~~~~~~~~~~ .. _qstyleoptionbutton-1: QStyleOptionButton ~~~~~~~~~~~~~~~~~~ QStyleOptionComplex ~~~~~~~~~~~~~~~~~~~ QStyleOptionDockWidget ~~~~~~~~~~~~~~~~~~~~~~ QStyleOptionFocusRect ~~~~~~~~~~~~~~~~~~~~~ QStyleOptionFrame ~~~~~~~~~~~~~~~~~ QStyleOptionGraphicsItem ~~~~~~~~~~~~~~~~~~~~~~~~ QStyleOptionHeader ~~~~~~~~~~~~~~~~~~ QStyleOptionMenuItem ~~~~~~~~~~~~~~~~~~~~ QStyleOptionProgressBar ~~~~~~~~~~~~~~~~~~~~~~~ QStyleOptionRubberBand ~~~~~~~~~~~~~~~~~~~~~~ QStyleOptionTab ~~~~~~~~~~~~~~~ QStyleOptionTabBarBase ~~~~~~~~~~~~~~~~~~~~~~ QStyleOptionTabWidgetFrame ~~~~~~~~~~~~~~~~~~~~~~~~~~ QStyleOptionToolBar ~~~~~~~~~~~~~~~~~~~ QStyleOptionToolBox ~~~~~~~~~~~~~~~~~~~ QStyleOptionViewItem ~~~~~~~~~~~~~~~~~~~~ QStyle ------ 帮助系统 ======== 请查阅C++ GUI Qt4编程(第二版)第17章 Qt国际化 ======== 多媒体 ====== 录音 ---- 使用QAudioRecorder录制音频 ~~~~~~~~~~~~~~~~~~~~~~~~~~ +--------------------------------------------------------+ | #ifndef MAINWINDOW_H | | | | #define MAINWINDOW_H | | | | #include | | | | #include | | | | #include | | | | #include | | | | #include | | | | namespace **Ui** { | | | | class **MainWindow**; | | | | } | | | | class **MainWindow** : public QMainWindow | | | | { | | | | Q_OBJECT | | | | public: | | | | explicit **MainWindow**\ (QWidget \*parent = nullptr); | | | | ~\ **MainWindow**\ (); | | | | void **audioRecord**\ (); | | | | void **startRecord**\ (); | | | | private: | | | | Ui::MainWindow \*ui; | | | | QAudioRecorder record; | | | | QString fileName; | | | | QLabel statusFileName; | | | | }; | | | | #endif // MAINWINDOW_H | +--------------------------------------------------------+ +----------------------------------------------------------------------+ | #include "mainwindow.h" | | | | #include "ui_mainwindow.h" | | | | MainWindow::**MainWindow**\ (QWidget \*parent) : | | | | QMainWindow(parent), | | | | ui(new Ui::MainWindow) | | | | { | | | | ui->setupUi(this); | | | | ui->statusBar->addWidget(&statusFileName); | | | | audioRecord(); | | | | connect(ui->action_Open,&QAction::triggered,[&](){ | | | | fileName=QFileDialog::getOpenFileName(this,tr("选择文件")); | | | | statusFileName.setText(fileName); | | | | }); | | | | connect(ui->btn_Stop,&QPushButton::clicked,[this](){ | | | | record.stop(); | | | | }); | | | | connect(ui->btn_Pause,&QPushButton::clicked,[this](){ | | | | record.pause(); | | | | }); | | | | connect | | (ui->btn_Start,&QPushButton::clicked,this,&MainWindow::startRecord); | | | | } | | | | MainWindow::~\ **MainWindow**\ () | | | | { | | | | delete ui; | | | | } | | | | void MainWindow::**audioRecord**\ () | | | | { | | | | foreach(auto&str,record.audioInputs()) | | | | ui->comBox_AudioList->addItem(str); | | | | connect(ui->btn_Start,&QPushButton::clicked,[&](){ | | | | }); | | | | QAudioEncoderSettings settings; | | | | settings.setBitRate(96000); | | | | settings.setCodec("audio/pcm"); | | | | settings.setQuality(QMultimedia::EncodingQuality::VeryHighQuality); | | | | settings.setChannelCount(8); | | | | record.setAudioSettings(settings); | | | | record.setAudioInput(record.defaultAudioInput()); | | | | } | | | | void MainWindow::**startRecord**\ () | | | | { | | | | record.setOutputLocation(QFileDialog::getOpenFileUrl()); | | | | record.record(); | | | | } | +----------------------------------------------------------------------+ |image33| 自定义插件和库 ============== 自定义Qt Designer Widget插件 ---------------------------- 加载ActiveX控件 --------------- QAxWidget Qt枚举 ====== 1. **Qt::TextElideMode** =============== == ============ 描述 值 解释 =============== == ============ Qt::ElideLeft 0 省略开头 Qt::ElideRight 1 省略结尾 Qt::ElideMiddle 2 省略中间 Qt::ElideNone 3 不显示省略号 =============== == ============ 2. **Qt::Corner** ===================== ======= ========== 描述 值 解释 ===================== ======= ========== Qt::TopLeftCorner 0x00000 矩形左上角 Qt::TopRightCorner 0x00001 矩形右上角 Qt::BottomLeftCorner 0x00002 矩形左下角 Qt::BottomRightCorner 0x00003 矩形右下角 ===================== ======= ========== 3. **Qt::DockWidgetArea** ======================== =================== ==== 描述 值 解释 ======================== =================== ==== Qt::LeftDockWidgetArea 0x1 Qt::RightDockWidgetArea 0x2 Qt::TopDockWidgetArea 0x4 Qt::BottomDockWidgetArea 0x8 Qt::AllDockWidgetAreas DockWidgetArea_Mask Qt::NoDockWidgetArea 0 ======================== =================== ==== 常见错误 ======== undefined reference to \`vtable for’ ------------------------------------ 方法一: ~~~~~~~~ “QT中,类要支持信号与槽机制,需要继承自QObject并在头文件开头添加Q_OBJECT宏. 如果使用QtCreator创建类时,没有选择继承自QObject类或其子类,而在创建后手工修改继承自QObject并手工添加Q_OBJECT宏,则在编译时有可能会出现”undefined reference to \`vtable for’…….”错误. 解决方法: 把新创建的类从项目中移除(主要不要从磁盘上删除),然后再添加进功能,QtCreator就会重新解析此类,再编译就不再会出现上述错误. :sup:`[2.]` 方法二: ~~~~~~~~ 使用QT编程时,当用户自定义了一个类,只要类中使用了信号或槽。Code::Blocks编译就会报错(undefined reference to \`vtable for)。Google上有很多这个问题的回答,但很多说的很模糊,或者根本就不可行.其实,QT有自己的编译方法. 不用IDE写一个类,QT的编译步骤是: 1. cd 源代码目录 2. qmake -project [20]_ 3. qmake project_name.pro 4. make (如果你装的是minGW的话,就用mingw32-make.exe)\ :sup:`[3.]` undefined reference to ---------------------- |image34| 错误原因:函数仅定义,未实现 incomplete type --------------- 完整错误显示如下: +----------------------------------------------------------------+ | widget.h:19:20: error: allocation of incomplete type 'QMenu' | | | | qabstractitemview.h:56:7: note: forward declaration of 'QMenu' | +----------------------------------------------------------------+ 原因:未包含QMenu的头文件。 connect没有匹配的函数 --------------------- |image35| 原因: 信号或槽有重载 使用msvc编译 ------------ |image36| 解决办法:msvc在编译UTF-8文件时,会将无BOM的文件当做本地编码(GB2312)处理,有BOM的才当作UTF-8文件,因此,,将文件改为UTF-8+BOM的即可: |image37| 然后重新保存文件。\ :sup:`[6.]` MSVC qDebug()输出乱码 --------------------- |image38| 解决方法:\ :sup:`[7.]` 在头文件内添加一句宏 [21]_: +------------------------------------------+ | #pragma execution_character_set("utf-8") | +------------------------------------------+ QDesktopServices打开中文路径乱码 -------------------------------- 代码如下: +----------------------------------------------------------------+ | void MainWindow::**openFile**\ (const QModelIndex &modelindex) | | | | { | | | | QString filePath=FileLists->at(modelindex.row()); | | | | qDebug()<`_ 2. `undefined reference to vtable for 问题的原因及解决方法 `_ 3. `Qt的“undefined reference to vtable for”错误解决(手动解决,加深理解) `_ 4. `Qt 学习之路 2(53):自定义拖放数据 `_ 5. C++ GUI Qt4编程(第二版) 6. `Qt系列 :用MSVC2015编译常见编译错误及解决方案 `_ 7. `Qt与MSVC中文乱码问题的解决方案_imxiangzi的专栏-CSDN博客 `_ .. [1] 事实上,调用event->ignore()也可以 .. [2] 被控制的空间都要安装事件过滤器 .. [3] 在调用processEvents时最好加上QeventLoop::ExcludeUserInputEvents参数以忽略鼠标和键盘事件,防止由于用户操作导致的不可知后果。 事实上,只有在使用qDebug()第二中输出方式时才需要包含头文件,只使用第一种时无需包含。 .. [5] 在Windows下,qDebug()将信息输出到调试器,而不是stdout。 .. [6] qCritical将信息打印到stderr。 .. [7] qFatal()函数将信息输出到stderr。 .. [8] qCritical将信息打印到stderr。 注:当QlistWidget显示模式为QListView::IconMode时,图标是可拖动的 .. [10] 注:当QlistWidget显示模式为QListView::IconMode时,图标是可拖动的 .. [11] 附录18.1 列举了大部分的MIME类型 .. [12] 当你在Win10下开启“剪贴板历史记录”时,你在剪切板中储存的数据可以使用win+V键在剪切板历史记录中看到(只支持部分数据)。 .. [13] 当separatorsCollapsible设置为true时,菜单开始、菜单结束的分割线会被隐藏,连续的分割线仅显示一个。 .. [14] 在Win10 1903 + Qt 5.12.3 环境下测试时,扁平化与非扁平化表现形式相同。 .. [15] 简单的一组按钮互斥可以通过设置autoExclusive属性实现。 该函数必须保证至少一个按钮的Checkable属性为true。 .. [18] 该函数必须保证至少一个按钮的Checkable属性为true。 .. [19] 该信号可用于实现右键菜单 .. [20] 若已有项目文件(.pro)此步骤可省略。 .. [21] 所有含中文的文件都要添加此宏,为了方便,将其写入头文件中。 该宏Mingw也支持 .. |image0| image:: assets/image1.png .. |image1| image:: assets/image2.png .. |image2| image:: assets/image3.png .. |image3| image:: assets/image4.png .. |image4| image:: assets/image3.png .. |image5| image:: assets/image5.png .. |image6| image:: assets/image6.png .. |image8| image:: assets/image8.png .. |image9| image:: assets/image9.png .. |image10| image:: assets/image10.png .. |image11| image:: assets/image11.png .. |image12| image:: assets/image12.png .. |image13| image:: assets/image13.png .. |image14| image:: assets/image14.png .. |image18| image:: assets/image18.jpg .. |image20| image:: assets/image20.png .. |image23| image:: assets/image23.png .. |image24| image:: assets/image24.png .. |image25| image:: assets/image25.png .. |image27| image:: assets/image27.png .. |image28| image:: assets/image28.png .. |image29| image:: assets/image29.png .. |image30| image:: assets/image30.png .. |image31| image:: assets/image31.png .. |image32| image:: assets/image32.png .. |image33| image:: assets/image33.png .. |image34| image:: assets/image34.png .. |image35| image:: assets/image35.png .. |image36| image:: assets/image36.png .. |image37| image:: assets/image37.png .. |image38| image:: assets/image38.png .. |image39| image:: assets/image39.png .. |ListWidget| image:: assets/image15.png .. |ListWidget2| image:: assets/image16.png .. |TableWidget| image:: assets/image17.png .. |TableWidget2| image:: assets/image19.png .. |TableWidget3| image:: assets/image21.png .. |Widget_NoUI_Test| image:: assets/image22.png .. |Widget_NoUI_Test2| image:: assets/image26.png .. |如何理解Qt的viewport视图区?| image:: assets/image40.jpg .. |如何理解Qt的viewport视图区?2| image:: assets/image41.jpg .. |如何理解Qt的viewport视图区?3| image:: assets/image42.jpg .. |如何理解Qt的viewport视图区?4| image:: assets/image43.jpg .. |如何理解Qt的viewport视图区?5| image:: assets/image44.jpg .. |如何理解Qt的viewport视图区?6| image:: assets/image45.jpg .. |如何理解Qt的viewport视图区?7| image:: assets/image46.jpg .. |如何理解Qt的viewport视图区?8| image:: assets/image47.jpg .. |如何理解Qt的viewport视图区?9| image:: assets/image48.jpg .. |如何理解Qt的viewport视图区?10| image:: assets/image49.jpg .. |如何理解Qt的viewport视图区?11| image:: assets/image50.jpg .. |如何理解Qt的viewport视图区?13| image:: assets/image52.jpg .. |这里写图片描述| image:: assets/image7.png